package com.example.shutdown_v1.service;

import com.example.shutdown_v1.dto.RecognitionResult;
import com.example.shutdown_v1.dto.SensorDataPacket;
import com.example.shutdown_v1.entity.Gateway;
import com.example.shutdown_v1.entity.Sensor;
import com.example.shutdown_v1.entity.SensorModel;
import com.example.shutdown_v1.entity.Shop;
import com.example.shutdown_v1.parser.SensorParserFactory;
import com.example.shutdown_v1.repository.GatewayRepository;
import com.example.shutdown_v1.repository.SensorRepository;
import com.example.shutdown_v1.repository.ShopRepository;
import com.google.gson.Gson;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * MQTT消息处理服务
 * 接收、解析并处理来自MQTT的传感器数据
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MqttMessageHandler {
    
    private final SensorParserFactory parserFactory;
    private final SensorDataStorageService storageService;
    private final ValveStateRecognitionService recognitionService;
    private final GatewayRepository gatewayRepository;
    private final SensorRepository sensorRepository;
    private final ShopRepository shopRepository;
    private final Gson gson = new Gson();
    
    // 【新增】注入Redis服务（可选）
    @Autowired(required = false)
    private RedisSensorDataService redisSensorDataService;
    
    // 【新增】注入电力设备服务（可选）
    @Autowired(required = false)
    private PowerDeviceService powerDeviceService;
    
    @Value("${data.storage.redis.enabled:false}")
    private boolean redisEnabled;
    
    @Value("${data.storage.mysql.save-state-change-only:false}")
    private boolean mysqlSaveStateChangeOnly;
    
    @Value("${data.storage.mysql.save-sensor-data-snapshot:true}")
    private boolean mysqlSaveSensorDataSnapshot;
    
    /**
     * 传感器数据处理限制（防止同一传感器1秒内重复处理）
     * Key: sensorId, Value: 最后处理时间戳
     */
    private final java.util.concurrent.ConcurrentHashMap<String, Long> sensorProcessLimit = 
            new java.util.concurrent.ConcurrentHashMap<>();
    
    /**
     * 处理MQTT消息（支持主题识别和网关映射）
     * @param message Spring Integration消息
     */
    @ServiceActivator(inputChannel = "mqttInputChannel")
    public void handleMessage(Message<?> message) {
        try {
            // 1. 获取消息主题（重要！用于识别来源网关）
            String receivedTopic = (String) message.getHeaders().get("mqtt_receivedTopic");
            
            // 【安全检查】验证主题是否存在
            if (receivedTopic == null || receivedTopic.isEmpty()) {
                log.warn("MQTT消息缺少主题信息，跳过处理");
                return;
            }
            
            // 1.5 判断消息类型 - 根据主题分类处理
            
            // 【昆仑网关消息】- GwData/{商家code} 格式
            if (receivedTopic.startsWith("GwData/")) {
                String merchantCode = extractMerchantCodeFromTopic(receivedTopic);
                 handleKunLunGatewayMessage(receivedTopic, merchantCode, message.getPayload());
                return;
            }
            
            // 【电力设备消息】- 十六进制格式
            if (receivedTopic.equals("/mallpower/post") || receivedTopic.startsWith("/mallpower/post/")) {
                String hexPayload = convertToHex(message.getPayload());
                log.info("收到电力设备消息 - 主题: {}, Hex: {}", receivedTopic, hexPayload);
                com.example.shutdown_v1.controller.MqttTestController.recordMessage(receivedTopic, hexPayload);
                handlePowerDeviceMessage(receivedTopic, hexPayload);
                return;
            }
            
            // 【旧网关消息已废弃】- mall/# 主题不再处理，只使用昆仑网关
            log.warn("收到未知主题消息，已忽略 - 主题: {}", receivedTopic);
            
        } catch (Exception e) {
            log.error("MQTT消息处理失败", e);
        }
    }
    
    /**
     * 将二进制数据转换为十六进制字符串（用于断路器消息）
     * @param payload MQTT payload (可能是 byte[] 或 String)
     * @return 十六进制字符串 (如 "F30031")
     */
    private String convertToHex(Object payload) {
        try {
            byte[] bytes;
            
            if (payload instanceof byte[]) {
                // 直接是字节数组
                bytes = (byte[]) payload;
            } else if (payload instanceof String) {
                // 字符串转字节数组
                bytes = ((String) payload).getBytes();
            } else {
                // 其他类型，尝试转为字符串再转字节数组
                bytes = payload.toString().getBytes();
            }
            
            // 字节数组转十六进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : bytes) {
                hexString.append(String.format("%02X", b & 0xFF));
            }
            
            return hexString.toString();
            
        } catch (Exception e) {
            log.error("二进制数据转Hex失败: {}", e.getMessage());
            return "";
        }
    }
    
    /**
     * 将字节数组转换为 UTF-8 字符串（用于传感器 JSON 消息）
     * @param payload MQTT payload (可能是 byte[] 或 String)
     * @return UTF-8 字符串
     */
    private String convertToString(Object payload) {
        try {
            if (payload instanceof byte[]) {
                // 字节数组转 UTF-8 字符串
                return new String((byte[]) payload, "UTF-8");
            } else if (payload instanceof String) {
                // 已经是字符串
                return (String) payload;
            } else {
                // 其他类型，直接转字符串
                return payload.toString();
            }
        } catch (Exception e) {
            log.error("字节数组转String失败: {}", e.getMessage());
            return "";
        }
    }
    
    /**
     * 处理电力设备MQTT消息（支持主动查询响应 + 被动状态上报）
     * @param topic MQTT主题
     * @param hexPayload 十六进制数据
     */
    private void handlePowerDeviceMessage(String topic, String hexPayload) {
        try {
            // 临时禁用被动监控，避免与主动查询冲突
            // if (passivePowerMonitorService != null) {
            //     passivePowerMonitorService.processStatusReport(topic, hexPayload);
            // }
            
            // 兼容原有主动查询模式
            if (powerDeviceService != null) {
                String merchantId = "default";
                if (topic.startsWith("/mallpower/post/")) {
                    String[] parts = topic.split("/");
                    if (parts.length >= 3) {
                        merchantId = parts[2];
                    }
                }
                
                log.info("[电力设备] 兼容主动查询模式 - 商家:{}, 主题:{}", merchantId, topic);
                powerDeviceService.handleDeviceResponse(merchantId, hexPayload);
            }
            
        } catch (Exception e) {
            log.error("[电力设备] 消息处理失败 - 主题:{}, 数据:{}", topic, hexPayload, e);
        }
    }
    
    /**
     * 从主题中提取商家code
     * 主题格式: GwData/{商家code}
     * @param topic MQTT主题
     * @return 商家code
     */
    private String extractMerchantCodeFromTopic(String topic) {
        try {
            if (topic.startsWith("GwData/")) {
                String[] parts = topic.split("/");
                if (parts.length >= 2) {
                    return parts[1];
                }
            }
            log.warn("无法从主题中提取商家code: {}", topic);
            return "unknown";
        } catch (Exception e) {
            log.error("提取商家code失败 - 主题:{}", topic, e);
            return "unknown";
        }
    }
    
    /**
     * 处理昆仑网关消息
     * @param topic MQTT主题 (GwData/{商家code})
     * @param merchantCode 商家code
     * @param payload 消息内容
     */
    private void handleKunLunGatewayMessage(String topic, String merchantCode, Object payload) {
        try {
            String payloadStr = convertToString(payload);
            
            // 记录原始消息
            com.example.shutdown_v1.controller.MqttTestController.recordMessage(topic, payloadStr);
            
            log.debug("[昆仑网关] 商家:{}, 主题:{}, 数据长度:{} 字节", 
                    merchantCode, topic, payloadStr.length());
            
            // 解析JSON为昆仑网关消息格式
            com.example.shutdown_v1.dto.KunLunGatewayMessage gatewayMsg;
            try {
                gatewayMsg = gson.fromJson(payloadStr, com.example.shutdown_v1.dto.KunLunGatewayMessage.class);
            } catch (com.google.gson.JsonSyntaxException e) {
                log.error("[昆仑网关] JSON解析失败 - 商家:{}", merchantCode, e);
                return;
            }
            
            if (gatewayMsg == null) {
                log.error("[昆仑网关] 消息解析为null - 商家:{}", merchantCode);
                return;
            }
            
            String pktType = gatewayMsg.getPkt_type();
            String gwAddr = gatewayMsg.getGw_addr();
            
            // 根据消息类型分类处理
            if ("state".equals(pktType)) {
                // 设备数据消息（一条消息一个传感器数据包）
                handleKunLunGatewayDeviceData(topic, merchantCode, gwAddr, gatewayMsg);
            } else if ("scan_report".equals(pktType)) {
                // 扫描报告消息：包含其他蓝牙设备，直接跳过不处理
                log.debug("[昆仑网关] 跳过scan_report消息 - 商家:{}, 网关:{}", merchantCode, gwAddr);
                return;
            } else {
                log.warn("[昆仑网关] 未知消息类型:{} - 商家:{}, 网关:{}", pktType, merchantCode, gwAddr);
            }
            
        } catch (Exception e) {
            log.error("[昆仑网关] 消息处理失败 - 主题:{}, 商家:{}", topic, merchantCode, e);
        }
    }
    
    /**
     * 处理昆仑网关的设备数据消息
     * @param topic MQTT主题
     * @param merchantCode 商家code（实际是Shop.code）
     * @param gwAddr 网关地址
     * @param gatewayMsg 网关消息
     */
    private void handleKunLunGatewayDeviceData(String topic, String merchantCode, String gwAddr, 
            com.example.shutdown_v1.dto.KunLunGatewayMessage gatewayMsg) {
        try {
            com.example.shutdown_v1.dto.KunLunGatewayMessage.DataPayload data = gatewayMsg.getData();
            if (data == null) {
                log.warn("Missing data payload, merchantCode={}, gateway={}", merchantCode, gwAddr);
                return;
            }
            
            String deviceAddr = data.getDevice_addr();
            com.example.shutdown_v1.dto.KunLunGatewayMessage.RecvInfos recvInfos = data.getRecv_infos();
            
            if (deviceAddr == null || recvInfos == null || recvInfos.getRaw() == null) {
                log.warn("Missing required fields, merchantCode={}, gateway={}", merchantCode, gwAddr);
                return;
            }
            
            // Rate limit: process same sensor at most once per second
            long currentTime = System.currentTimeMillis();
            Long lastProcessTime = sensorProcessLimit.get(deviceAddr);
            if (lastProcessTime != null && (currentTime - lastProcessTime) < 1000) {
                log.debug("Skip duplicate processing, deviceAddr={}, interval={}ms", 
                        deviceAddr, (currentTime - lastProcessTime));
                return;
            }
            sensorProcessLimit.put(deviceAddr, currentTime);
            
            String rawData = recvInfos.getRaw();
            log.info("Processing device data, merchantCode={}, gateway={}, device={}, rawLen={}", 
                    merchantCode, gwAddr, deviceAddr, rawData.length());
            
            Optional<Shop> shopOpt = shopRepository.findByCode(merchantCode);
            Long shopId = shopOpt.map(Shop::getId).orElse(null);
            String shopName = shopOpt.map(Shop::getName).orElse("unknown");
            
            if (shopId == null) {
                log.warn("Shop not found, code={}", merchantCode);
            }
            
            Optional<Sensor> sensorOpt = sensorRepository.findBySensorId(deviceAddr);
            Sensor sensor;
            
            if (!sensorOpt.isPresent()) {
                log.info("Auto-registering sensor, shop={}, deviceAddr={}", shopName, deviceAddr);
                sensor = new Sensor();
                sensor.setSensorId(deviceAddr);
                sensor.setShopId(shopId);
                sensor.setInstallPosition("unknown");
                sensor.setInstallPositionName(shopId != null ? shopName + "-pending" : "unassigned");
                sensor.setSensorModel("IM600");
                sensor.setStatus(1);
                sensor.setIsOffline(false);
                sensor.setLastOnlineTime(LocalDateTime.now());
                sensorRepository.save(sensor);
            } else {
                sensor = sensorOpt.get();
                if (sensor.getShopId() == null && shopId != null) {
                    sensor.setShopId(shopId);
                    sensor.setInstallPositionName(shopName + "-auto-linked");
                    log.info("Auto-linking sensor to shop, deviceAddr={}, shop={}", deviceAddr, shopName);
                }
            }
            
            sensor.setIsOffline(false);
            sensor.setLastOnlineTime(LocalDateTime.now());
            sensorRepository.save(sensor);
            
            SensorModel modelHint = SensorModel.IM600;
            SensorDataPacket sensorData = parserFactory.parse(rawData, deviceAddr, modelHint);
            
            if (sensorData == null) {
                log.warn("Parse failed, merchantCode={}, deviceAddr={}", merchantCode, deviceAddr);
                return;
            }
            
            log.debug("Parsed data, deviceAddr={}, euler=[R={}, P={}, Y={}]", 
                    deviceAddr, sensorData.getRoll(), sensorData.getPitch(), sensorData.getYaw());
            
            // 2.5. RSSI信号强度（如果state消息中包含）
            // 注：scan_report已跳过，RSSI需要从state消息本身获取
            
            RecognitionResult result = recognitionService.recognizeState(sensorData, sensor);
            log.info("State recognition result, deviceAddr={}, success={}, state={}", 
                    deviceAddr, result != null, result != null ? result.getState() : "null");
            
            // 3.5. 计算综合圈数差值（仅在启用多圈检测时计算）
            Integer useMultiTurn = sensor.getUseMultiTurnDetection();
            if (useMultiTurn != null && useMultiTurn == 1 &&
                sensorData.getRollTurnCount() != null && 
                sensorData.getYawTurnCount() != null) {
                
                // 获取基准值
                Double baselineRollTurn = sensor.getRollTurnBaselineClosed();
                Double baselineYawTurn = sensor.getYawTurnBaselineClosed();
                
                double compositeRotationCount;
                
                if (baselineRollTurn != null && baselineYawTurn != null) {
                    // 计算与基准值的差值
                    double diffRollTurn = Math.abs(sensorData.getRollTurnCount() - baselineRollTurn);
                    double diffYawTurn = Math.abs(sensorData.getYawTurnCount() - baselineYawTurn);
                    
                    compositeRotationCount = diffRollTurn;
                    
                    log.debug("Composite turn count diff, deviceAddr={}, value={}, rollDiff={}, yawDiff={}", 
                            deviceAddr, compositeRotationCount, diffRollTurn, diffYawTurn);
                } else {
                    compositeRotationCount = Math.sqrt(
                        Math.pow(sensorData.getRollTurnCount(), 2) +
                        Math.pow(sensorData.getYawTurnCount(), 2)
                    );
                    
                    log.debug("Composite turn count (no baseline), deviceAddr={}, value={}", 
                            deviceAddr, compositeRotationCount);
                }
                
                sensorData.setCompositeRotationCount(compositeRotationCount);
            } else {
                log.debug("Multi-turn not enabled, skip turn count calc, deviceAddr={}", deviceAddr);
            }
            
            // 4. 数据存储
            storageService.saveSensorData(sensorData);
            
            // 5. 保存到Redis（如果启用）
            if (redisEnabled && redisSensorDataService != null) {
                // 5.1 保存传感器数据流
                redisSensorDataService.saveSensorData(sensorData);
                
                if (result != null && result.getState() != null) {
                    redisSensorDataService.saveSensorStatus(
                        deviceAddr,
                        result.getState(),
                        result.getMinDistance(),
                        result.getConfidence(),
                        sensorData
                    );
                    log.info("Redis status saved, deviceAddr={}, state={}, confidence={}", 
                            deviceAddr, result.getState(), result.getConfidence());
                }
            }
            
            log.debug("Processing complete, merchantCode={}, deviceAddr={}, state={}, confidence={}", 
                    merchantCode, deviceAddr, 
                    result != null ? result.getState() : "unknown", 
                    result != null ? result.getConfidence() : 0.0);
            
        } catch (Exception e) {
            log.error("Device data processing failed, merchantCode={}, gateway={}", merchantCode, gwAddr, e);
        }
    }
}

